package com.iqiju.guagua.redtone.video.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.iqiju.guagua.redtone.video.common.Results;
import com.iqiju.guagua.redtone.video.mapper.WxRedtoneVideoTypeMapper;
import com.iqiju.guagua.redtone.video.model.WxMiniApp;
import com.iqiju.guagua.redtone.video.model.WxRedtoneVideoType;
import com.iqiju.guagua.redtone.video.utils.StringUtils;
import com.iqiju.guagua.redtone.video.vo.VideoListFirstVO;
import com.iqiju.guagua.redtone.video.vo.VideoTypeVO;
import com.iqiju.guagua.redtone.video.vo.VideoVO;
import com.iqiju.guagua.redtone.video.vo.VideoViewVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class WxRedtoneVideoTypeService extends ServiceImpl<WxRedtoneVideoTypeMapper, WxRedtoneVideoType> {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private WxMiniAppService wxMiniAppService;

    @Autowired
    private IDService idService;

    @Autowired
    private WxVideoTypesService wxVideoTypesService;

    @Autowired
    private WxMiniVideoService wxMiniVideoService;

    @Autowired
    private WxMiniAppConfigService wxMiniAppConfigService;


    /*=========== 视频列表分页查询 =====================================================================================*/

    public Results<VideoListFirstVO> selectPage(String wxMiniAppId, String type, String searchId, String num) {
        if (StringUtils.isNull(wxMiniAppId)){
            log.error("参数 wxMiniAppId 为空");
            return Results.error("参数 wxMiniAppId 不能为空");
        }
        Long miniAppId = null;
        try {
            miniAppId = Long.parseLong(wxMiniAppId);
        } catch (NumberFormatException e) {
            log.error("wxMiniAppId 转换成long 异常！wxMiniAppId={}", wxMiniAppId);
            log.error(e.getMessage(), e);
            return Results.error("参数 wxMiniAppId 不合法");
        }
        WxMiniApp wxMiniApp = wxMiniAppService.selectById(miniAppId);
        if (wxMiniApp==null){
            log.error("找不到wxMiniAppId=[{}] 的小程序！", wxMiniAppId);
            return Results.error("参数 wxMiniAppId 不合法");
        }

        if (StringUtils.isNotNull(type)){
            if (StringUtils.isNotNull(searchId)){
                if (StringUtils.isNotNull(num)){
                    log.info("111---精确打开某个类型的 非第一页");
                    Integer pageNum = null;
                    try {
                        pageNum = Integer.valueOf(num);
                    } catch (NumberFormatException e) {
                        log.error("num 转换成 int 异常！num={}", num);
                        log.error(e.getMessage(), e);
                        return Results.paramError("参数 num 不合法 不是正常数字");
                    }
                    if (pageNum<=1||pageNum>VideoListFirstVO.MAX_PAGE_NUM){
                        log.error("num范围不合法！不是正常范围内的页码! num={}", num);
                        return Results.paramError("参数 num 不合法 不是正常范围内的页码");
                    }
                    return openOneTypeOnePage(type,searchId,num, Long.parseLong(wxMiniAppId));
                }else{
                    log.error("110---传参错误  参数num为空！");
                    return Results.paramError("参数 num 不能为空");
                }
            }else{
                if (StringUtils.isNotNull(num)){
                    log.error("101---传参错误  参数searchId为空！");
                    return Results.paramError("参数 searchId 不能为空");
                }else{
                    log.info("100---精确打开某个类型的第一页");
                    return openOneTypeFirstPage(type, Long.parseLong(wxMiniAppId));
                }
            }
        }else{
            if (StringUtils.isNotNull(searchId)){
                if (StringUtils.isNotNull(num)){
                    log.error("011---传参错误  参数type为空！");
                    return Results.paramError("参数 type 不能为空");
                }else{
                    log.error("010---传参错误  参数type与num为空！");
                    return Results.paramError("参数 type和num 不能为空");
                }
            }else{
                if (StringUtils.isNotNull(num)){
                    log.error("001---传参错误  参数type与searchId为空！");
                    return Results.paramError("参数 type和searchId 不能为空");
                }else{
                    log.info("000---首次打开视频列表页面");
                    return firstOpenPage(Long.parseLong(wxMiniAppId));
                }
            }
        }
    }


    /*=========== 分页查询的三种情况 ===================================================================================*/


    /** 精确打开某个类型的某一页 */
    private Results<VideoListFirstVO> openOneTypeOnePage(String type, String searchId, String num, long wxMiniAppId) {
        if (!haveType(type)){
            log.error("参数 type 不合法 不存在此类型的type={}", type);
            return Results.paramError("参数 type 不合法 不存在此类型的type={}");
        }
        if (!haveSearchId(searchId)){
            log.error("参数 searchId 不合法  不存在此searchId={}", searchId);
            return Results.paramError("参数 searchId 不合法 不存在此searchId");
        }
        if (haveSearch(searchId,num)){
            log.error("参数不合法,符合当前条件的数据已经查询过！type=[{}] searchId=[{}]  num=[{}]  wxMiniAppId=[{}]",type,searchId,num,wxMiniAppId);
            return Results.paramError("参数不合法,符合当前条件的数据已经查询过！");
        }
        VideoListFirstVO videoListFirstVO = new VideoListFirstVO();
        //设置页码
        videoListFirstVO.setPageNum(num);
        //设置查询事务id
        videoListFirstVO.setSearchId(searchId);
        //查询指定类型（type）的第 num 页视频数据列表
        videoListFirstVO.setVideoList(selectVideoList(Long.parseLong(type), Long.parseLong(searchId), Integer.parseInt(num),wxMiniAppId));
        return Results.success(videoListFirstVO);
    }




    /** 精确打开某个类型的第一页 */
    private Results<VideoListFirstVO> openOneTypeFirstPage(String type,long wxMiniAppId) {
        if (!haveType(type)){
            log.error("参数 type 不合法 不存在此类型的type={}", type);
            return Results.paramError("参数 type 不合法 不存在此类型的type");
        }
        VideoListFirstVO videoListFirstVO = new VideoListFirstVO();
        //设置每页记录数
        videoListFirstVO.setPageSize(VideoListFirstVO.PAGE_SIZE_STRING);
        //设置页码
        videoListFirstVO.setPageNum("1");
        long searchId = idService.id();
        //设置查询事务id
        videoListFirstVO.setSearchId(String.valueOf(searchId));
        //查询指定类型（type）的第一页视频数据列表
        videoListFirstVO.setVideoList(selectVideoList(Long.parseLong(type),searchId,1,wxMiniAppId));
        return Results.success(videoListFirstVO);
    }

    /** 首次打开视频列表页面 */
    private Results<VideoListFirstVO> firstOpenPage(long wxMiniAppId) {
        VideoListFirstVO videoListFirstVO = new VideoListFirstVO();
        //查询首页自定义的视频类型列表
        List<VideoTypeVO> typeList = selectFirstVideoTypeList();
        videoListFirstVO.setTypeList(typeList);
        //设置每页记录数
        videoListFirstVO.setPageSize(VideoListFirstVO.PAGE_SIZE_STRING);
        //设置页码
        videoListFirstVO.setPageNum("1");
        long searchId = idService.id();
        //设置查询事务id
        videoListFirstVO.setSearchId(String.valueOf(searchId));
        //查询第一个类型（推荐）的第一页视频数据列表
        videoListFirstVO.setVideoList(selectVideoList(typeList.get(0).getId(),searchId,1,wxMiniAppId));
        return Results.success(videoListFirstVO);
    }

    /** 查询首页自定义的视频类型列表 */
    private List<VideoTypeVO> selectFirstVideoTypeList(){
        List<VideoTypeVO> typeList = new ArrayList<>();
        String firstValue = stringRedisTemplate.opsForValue().get(getFirstVideoTypeListKey());
        if (StringUtils.isNotNull(firstValue)){
            typeList = JSON.parseArray(firstValue,VideoTypeVO.class);
            return typeList;
        }

        QueryWrapper<WxRedtoneVideoType> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("type",1);
        queryWrapper.eq("enable",1);
        queryWrapper.orderByAsc("order_num");
        List<WxRedtoneVideoType> wxRedtoneVideoTypes = list(queryWrapper);


        for (int i = 0; i < wxRedtoneVideoTypes.size(); i++) {
            VideoTypeVO videoTypeVO = new VideoTypeVO();
            videoTypeVO.setId(wxRedtoneVideoTypes.get(i).getId());
            videoTypeVO.setName(wxRedtoneVideoTypes.get(i).getName());
            if (i==0){
                videoTypeVO.setActive(1);
            }else{
                videoTypeVO.setActive(0);
            }
            typeList.add(videoTypeVO);
        }

        stringRedisTemplate.opsForValue().set(getFirstVideoTypeListKey(), JSON.toJSONString(typeList),30, TimeUnit.DAYS);
        return typeList;
    }
    private String getFirstVideoTypeListKey(){
        return "REDTONE_VIDEO_MINI_APP_FIRST_INTO_TYPE_LIST";
    }

    /*===============================================================================================================*/








    /*=========== 查询参数合法性判断 ===================================================================================*/

    /** 判断是否存在自定义视频类型 */
    private boolean haveType(String typeIdString){
        Boolean isHave = stringRedisTemplate.hasKey(getVideoTypeKey(typeIdString));
        if (isHave){
            return true;
        }
        WxRedtoneVideoType wxRedtoneVideoType = getById(typeIdString);
        if (wxRedtoneVideoType==null){
            return false;
        }
        Long typeId = wxRedtoneVideoType.getId();
        stringRedisTemplate.opsForValue().set(getVideoTypeKey(typeId),"1",30,TimeUnit.DAYS);
        return true;
    }
    private String getVideoTypeKey(Object typeId) {
        return "REDTONE_VIDEO_LOCAL_TYPE_" + typeId;
    }

    /** 判断是否存在 查询事务id */
    private boolean haveSearchId(String searchId) {
        return stringRedisTemplate.hasKey(getSearchIdKey(searchId));
    }
    private String getSearchIdKey(Object searchId) {
        return "REDTONE_VIDEO_SEARCH_ID_" + searchId;
    }

    /** 判断此条件下的查询是否已经访问过，返回false表示未查询过 返回true表示已查询过或者查询不合法 */
    private boolean haveSearch(String searchId, String num) {
        String key = getSearchIdKey(searchId);
        int pageNum = Integer.parseInt(num);
        for (int i = pageNum; i <= VideoListFirstVO.MAX_PAGE_NUM; i++) {
            Boolean isHave = stringRedisTemplate.opsForHash().hasKey(key, String.valueOf(i));
            if (isHave){
                return true;
            }
        }
        for (int i = pageNum-1; i >= 1; i--) {
            Boolean isHave = stringRedisTemplate.opsForHash().hasKey(key, String.valueOf(i));
            if (!isHave){
                return true;
            }
        }
        return false;
    }
    /*===============================================================================================================*/






    /*=========== 查询视频列表 ========================================================================================*/

    /** 根据类型，查询事务id和页码  查询视频列表 */
    private List<VideoVO> selectVideoList(long typeId, long searchId, int pageNum,long wxMiniAppId) {
        //根据自定义视频类型 查询所有最终对应的视频ids
        List<Long> videoIds = wxVideoTypesService.selectVideoIdsByLocalTypeId(typeId);
        //查询此searchId下所有已经查询过的视频ids
        List<Long> haveSearchIds = selectHaveSearchVideoIdsBySearchId(searchId);
        //根据所有视频ids和已经查询过的ids，查询下一页不重复的视频记录
        List<VideoVO> videoVOS = selectNoRepeatVideos(videoIds, haveSearchIds, searchId, pageNum);

        //判断广告是否开启，如果开启，加广告
        String adEnable = wxMiniAppConfigService.selectByTypeAndKey(wxMiniAppId, "VIDEO_LIST_AD", "ENABLE");
        if ("1".equals(adEnable)){
            boolean bannerEnable = false;
            boolean videoEnable = false;
            String videoAdId = wxMiniAppConfigService.selectByTypeAndKey(wxMiniAppId, "VIDEO_LIST_AD", "VIDEO_AD_ID");
            if (videoAdId!=null&&videoAdId.trim().length()>0){
                videoEnable = true;
            }
            String bannerAdId = wxMiniAppConfigService.selectByTypeAndKey(wxMiniAppId, "VIDEO_LIST_AD", "BANNER_AD_ID");
            if (bannerAdId!=null&&bannerAdId.trim().length()>0){
                bannerEnable = true;
            }
            for (int i = VideoListFirstVO.PAGE_SIZE-1 ; i >= 0 ; i--) {
                if (bannerEnable&&(i%5==1)&&((i/5)%2==1)){
                    VideoVO videoVO = new VideoVO();
                    videoVO.setType(2);
                    videoVO.setAdId(bannerAdId);
                    videoVO.setAdType(1);
                    videoVOS.add(i,videoVO);
                }
                if (videoEnable&&(i%5==1)&&((i/5)%2==0)){
                    VideoVO videoVO = new VideoVO();
                    videoVO.setType(2);
                    videoVO.setAdId(videoAdId);
                    videoVO.setAdType(2);
                    videoVOS.add(i,videoVO);
                }
            }
        }
        return videoVOS;
    }

    /** 根据所有视频ids和已经查询过的ids，查询下一页不重复的视频记录 */
    private List<VideoVO> selectNoRepeatVideos(List<Long> videoIds, List<Long> haveSearchIds, long searchId, int pageNum) {
        int size = videoIds.size();
        List<Long> ids = new ArrayList<>();
        for (int i = 0; i < VideoListFirstVO.PAGE_SIZE; i++) {
            //从全集中随机取出一个视频id
            int index = new Random().nextInt(size);
            Long randomId = videoIds.get(index);
            //判断历史已查询和本次已查询中是否包含，如果包含，从新取出一个
            if (ids.contains(randomId)||haveSearchIds.contains(randomId)){
                i--;
                continue;
            }
            //查询到一个未查询过的，放到列表中
            ids.add(videoIds.get(index));
        }
        //每查询出一页数据，就放入缓存中，并将查询id从新设置为12小时过期
        stringRedisTemplate.opsForHash().put(getSearchIdKey(searchId),String.valueOf(pageNum),JSON.toJSONString(ids));
        stringRedisTemplate.expire(getSearchIdKey(searchId), 12, TimeUnit.HOURS);
        //根据新的一页视频ids，查询并返回 视频记录
        return wxMiniVideoService.selectListByIds(ids);
    }

    /**  查询此searchId下所有已经查询过的视频ids  */
    private List<Long> selectHaveSearchVideoIdsBySearchId(long searchId) {
        String key = getSearchIdKey(searchId);
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(key);
        List<Long> haveSearchIds = new ArrayList<>();
        entries.forEach((k,v)->{
            List<Long> ids = JSON.parseArray(String.valueOf(v), Long.class);
            haveSearchIds.addAll(ids);
        });
        return haveSearchIds;
    }

    /*===============================================================================================================*/




































    /*=========== 视频播放页详细内容查询 ================================================================================*/

    public Results<VideoViewVO> view(String wxMiniAppId) {
        if (StringUtils.isNull(wxMiniAppId)){
            log.error("参数 wxMiniAppId 为空");
            return Results.error("参数 wxMiniAppId 不能为空");
        }
        Long miniAppId = null;
        try {
            miniAppId = Long.parseLong(wxMiniAppId);
        } catch (NumberFormatException e) {
            log.error("wxMiniAppId 转换成long 异常！wxMiniAppId={}", wxMiniAppId);
            log.error(e.getMessage(), e);
            return Results.error("参数 wxMiniAppId 不合法");
        }
        WxMiniApp wxMiniApp = wxMiniAppService.selectById(miniAppId);
        if (wxMiniApp==null){
            log.error("找不到wxMiniAppId=[{}] 的小程序！", wxMiniAppId);
            return Results.error("参数 wxMiniAppId 不合法");
        }

        //查询广告是否开启，根据是否开启添加广告
        String adEnable = wxMiniAppConfigService.selectByTypeAndKey(miniAppId, "VIDEO_LIST_AD", "ENABLE");
        boolean bannerAdEnable = false;
        boolean videoAdEnable = false;
        boolean frontAdEnable = false;
        String bannerAdId = null;
        String videoAdId = null;
        String frontAdId = null;
        if ("1".equals(adEnable)){
            bannerAdId = wxMiniAppConfigService.selectByTypeAndKey(miniAppId, "VIDEO_LIST_AD", "BANNER_AD_ID");
            if (StringUtils.isNotNull(bannerAdId)){
                bannerAdEnable = true;
            }
            videoAdId = wxMiniAppConfigService.selectByTypeAndKey(miniAppId, "VIDEO_LIST_AD", "VIDEO_AD_ID");
            if (StringUtils.isNotNull(videoAdId)){
                videoAdEnable = true;
            }
            frontAdId = wxMiniAppConfigService.selectByTypeAndKey(miniAppId, "VIDEO_LIST_AD", "FRONT_AD_ID");
            if (StringUtils.isNotNull(frontAdId)){
                frontAdEnable = true;
            }
        }
        VideoViewVO videoViewVO = new VideoViewVO();
        //如果banner广告可用，就配置banner广告
        if (bannerAdEnable){
            videoViewVO.getTopAd().setEnable(1);
            videoViewVO.getTopAd().setId(bannerAdId);
            videoViewVO.getTopAd().setType(1);
        }
        //如果前贴广告可用，就配置前贴广告
        if (frontAdEnable){
            videoViewVO.getFrontAd().setEnable(1);
            videoViewVO.getFrontAd().setId(frontAdId);
            videoViewVO.getFrontAd().setType(3);
        }
        //查询视频播放页面的推荐视频列表
        List<VideoVO> videoVOS = selectVideoPlayList();

        //在视频列表中的对应位置配置banner广告和视频广告
        for (int i = videoVOS.size()-1; i >=0 ; i--) {
            if (bannerAdEnable&&i%5==0&&((i/5)%2)==1){
                VideoVO videoVO = new VideoVO();
                videoVO.setType(2);
                videoVO.setAdId(bannerAdId);
                videoVO.setAdType(1);
                videoVOS.add(i,videoVO);
            }
            if (videoAdEnable&&i%5==0&&((i/5)%2)==0){
                VideoVO videoVO = new VideoVO();
                videoVO.setType(2);
                videoVO.setAdId(videoAdId);
                videoVO.setAdType(2);
                videoVOS.add(i,videoVO);
            }
        }
        videoViewVO.setVideoList(videoVOS);

        //判断微信小程序跳转是否可用，如果可用就配置跳转小程序信息
        String toWxEnable = wxMiniAppConfigService.selectByTypeAndKey(miniAppId, "VIDEO_LIST_TO_WX_MINI", "ENABLE");
        if ("1".equals(toWxEnable)){
            String toWxUrl = wxMiniAppConfigService.selectByTypeAndKey(Long.valueOf(wxMiniAppId), "VIDEO_LIST_TO_WX_MINI", "URL");
            if (toWxUrl!=null&&toWxUrl.trim().length()>0){
                videoViewVO.getToWxUrlVO().setUrl(toWxUrl);
                String toWxOpenid = wxMiniAppConfigService.selectByTypeAndKey(Long.valueOf(wxMiniAppId), "VIDEO_LIST_TO_WX_MINI", "OPENID");
                if (toWxOpenid!=null&&toWxOpenid.trim().length()>0){
                    videoViewVO.getToWxUrlVO().setOpenid(toWxOpenid);
                    videoViewVO.getToWxUrlVO().setEnable(1);
                }
            }
        }
        return Results.success(videoViewVO);
    }



    /**  查询视频播放页面的推荐视频列表  */
    private List<VideoVO> selectVideoPlayList() {
        Long localTypeId = null;
        String playListIdValue = stringRedisTemplate.opsForValue().get(getPlayListVideoTypeKey());
        if (StringUtils.isNotNull(playListIdValue)){
            localTypeId = Long.valueOf(playListIdValue);
        }else{
            QueryWrapper<WxRedtoneVideoType> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("type",2);
            queryWrapper.eq("enable",1);
            List<WxRedtoneVideoType> wxRedtoneVideoTypes = list(queryWrapper);
            localTypeId = wxRedtoneVideoTypes.get(0).getId();
            stringRedisTemplate.opsForValue().set(getPlayListVideoTypeKey(),String.valueOf(localTypeId),30,TimeUnit.DAYS);
        }
        List<Long> videoIds = wxVideoTypesService.selectVideoIdsByLocalTypeId(localTypeId);
        //查询并返回推荐视频列表
        return selectNoRepeatVideos(videoIds);
    }

    private String getPlayListVideoTypeKey(){
        return "REDTONE_VIDEO_MINI_APP_PLAY_VIDEO_LIST_ID";
    }

    /** 查询不重复的推荐视频列表 */
    private List<VideoVO> selectNoRepeatVideos(List<Long> videoIds) {
        int size = videoIds.size();
        List<Long> ids = new ArrayList<>();
        for (int i = 0; i < VideoListFirstVO.PAGE_SIZE; i++) {
            int index = new Random().nextInt(size);
            if (ids.contains(videoIds.get(index))){
                i--;
                continue;
            }
            ids.add(videoIds.get(index));
        }
        return wxMiniVideoService.selectListByIds(ids);
    }

}
